package com.awanp3.nimbus.common.mybatis.aspect;

import com.awanp3.nimbus.common.core.constant.HeaderConstants;
import com.awanp3.nimbus.common.core.enums.FormatsType;
import com.awanp3.nimbus.common.core.utils.ObjectUtils;
import com.awanp3.nimbus.common.mybatis.annotation.TimeZoneNormalize;
import com.awanp3.nimbus.common.mybatis.core.domain.BaseEntity;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.TimeZone;
import java.util.stream.Stream;

/**
 * 转换时间
 * 仅针对BaseEntity.param中的字符串参数
 *
 * 注：代码转成JVM时区，实际应该转成数据库的时区。因此需要确保应用与数据库在同一时区。
 * @author 20014170
 */
@Aspect
@AutoConfiguration
@RequiredArgsConstructor
public class TimeZoneNormalizeAspect {

    private final HttpServletRequest request;
    private static final String[] PARAM_KEYS = new String[]{"beginTime", "endTime"};
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(FormatsType.YYYY_MM_DD_HH_MM_SS.getTimeFormat());
    /*
     * 切面
     */
    @Around("@annotation(timezoneNormalize)")
    public Object convertTimezone(ProceedingJoinPoint joinPoint, TimeZoneNormalize timezoneNormalize) throws Throwable {
        Object[] args = joinPoint.getArgs();
        Object[] newArgs = Arrays.stream(args)
            .map(arg -> {
                if (arg instanceof BaseEntity) {
                    return convertArgs((BaseEntity) arg, timezoneNormalize);
                }
                return arg;
            })
            .toArray();
        return joinPoint.proceed(newArgs);
    }

    /*
     * 参数转换
     */
    private BaseEntity convertArgs(BaseEntity entity, TimeZoneNormalize anno) {
        String clientTimezone = request.getHeader(HeaderConstants.TIME_ZONE_KEY);
        ZoneId fromZoneId = ZoneId.of(clientTimezone);
        ZoneId toZoneId = ZoneId.systemDefault();
//        if (ObjectUtils.isNotEmpty(entity.getCreateTime())) {
//            entity.setCreateTime(convertDate(entity.getCreateTime(), fromZoneId, toZoneId));
//        }
//        if (ObjectUtils.isNotEmpty(entity.getUpdateTime())) {
//            entity.setUpdateTime(convertDate(entity.getUpdateTime(), fromZoneId, toZoneId));
//        }
        if (ObjectUtils.isNotEmpty(entity.getParams())) {
            Stream.concat(Arrays.stream(PARAM_KEYS), Arrays.stream(anno.keys().split(",")))
                .distinct()
                .forEach(key -> {
                    if (ObjectUtils.isNotEmpty(entity.getParams().get(key))) {
                        entity.getParams().put(key, convertDateStr(entity.getParams().get(key).toString(), fromZoneId, toZoneId));
                    }
                });
        }
        return entity;
    }

    /**
     * 日期按时区转换， DateUtils.toDate
     */
    private Date convertDate(Date date, ZoneId fromZoneId, ZoneId toZoneId) {
        ZonedDateTime fromZoned = date.toInstant().atZone(fromZoneId);
        ZonedDateTime toZoned = fromZoned.withZoneSameInstant(toZoneId);
        return Date.from(toZoned.toInstant());
    }

    /**
     * 字符串按时区转换
     */
    private String convertDateStr(String dateStr, ZoneId fromZoneId, ZoneId toZoneId) {
        LocalDateTime ldt = LocalDateTime.parse(dateStr, DATE_TIME_FORMATTER);
        ZonedDateTime fromZdt = ldt.atZone(fromZoneId);
        ZonedDateTime toZdt = fromZdt.withZoneSameInstant(toZoneId);
        return toZdt.format(DATE_TIME_FORMATTER);
    }

    private String convertDateStr(String dateStr, TimeZone fromZone, TimeZone toZone) {
        return convertDateStr(dateStr, ZoneId.of(fromZone.getID()), ZoneId.of(toZone.getID()));
    }
}
